package jdk8.Lambda;

import org.junit.jupiter.api.Test;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;

import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Demo08Supplier {
    private static String getString(Supplier<String> function) {
        return function.get();
    }

    /**
     * Supplier 无参数，指定返回值类型，经常用于只注重过程的代码
     * @param
     */
    @Test
    public void   supplier() {
        String msgA = "Hello";
        String msgB = "World";
        System.out.println(getString(() -> msgA + msgB));
    }

    @Test
    public void   Demo02Test () {
        int arr[] = {2, 3, 4, 52, 333, 23};
        int maxNum = getMax(() -> {
            //计算数组的最大值
            int max = arr[0];
            for (int i : arr) {
                if (i > max) {
                    max = i;
                }
            }
            return max;
        });
        System.out.println(maxNum);

    }
    private static int getMax(Supplier<Integer> sup){
        return sup.get();
    };

    /**
     * Consumer接口
     * java.util.function.Consumer<T> 接口则正好与Supplier接口相反，它不是生产一个数据，而是消费一个数据，
     * 其数据类型由泛型决定。
     */
    @Test
    void Demo09Consumer(){
        consumeString(s -> System.out.println(s));
    }
    private static void consumeString(Consumer<String> function) {
        function.accept("Hello");
    }



    /**
     * 默认方法：andThen
     * 如果一个方法的参数和返回值全都是 Consumer 类型，那么就可以实现效果：消费数据的时候，首先做一个操作，然后再做一个操作，实现组合。
     * 而这个方法就是 Consumer 接口中的default方法 andThen
     * 要想实现组合，需要两个或多个Lambda表达式即可，而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况
     */
    @Test
    void Demo10ConsumerAndThen(){
        consumeString(s -> System.out.println(s.toUpperCase()), s -> System.out.println(s.toLowerCase()));
    }
    private static void consumeString(Consumer<String> one, Consumer<String> two) {
        one.andThen(two).accept("Hello");
    }
@Test
    void demoConsumer(){
        String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
        printInfo(s -> System.out.print("姓名：" + s.split(",")[0]), s ->
                System.out.println("。性别：" + s.split(",")[1] + "。"), array);
    }

    private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
        for (String info : array) {
            one.andThen(two).accept(info); // 姓名：迪丽热巴。性别：女。
        }
    }

    /**
     *  Predicate接口
     *有时候我们需要对某种类型的数据进行判断，从而得到一个boolean值结果。
     * 这时可以使用java.util.function.Predicate<T> 接口。
     * 抽象方法：test
     * Predicate 接口中包含一个抽象方法： boolean test(T t) 。用于条件判断的场景：
     */
    @Test
    public  void demo15PredicateTest () {
        method(s -> s.length() > 5);

    }

    private static void method(Predicate<String> predicate) {
        boolean veryLong = predicate.test("HelloWorld");
        System.out.println("字符串很长吗：" + veryLong);
    }


    /**
     * 默认方法：and
     * 既然是条件判断，就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时，可以使用default方法 and
     * 如果要判断一个字符串既要包含大写“H”，又要包含大写“W”
     */
    @Test
    void demo16PredicateAnd(){
        method(s -> s.contains("H"), s -> s.contains("W"));
        methodnot(s -> s.contains("H"), s -> s.contains("W"));

    }

    private static void method(Predicate<String> one, Predicate<String> two) {
        boolean isValid = one.and(two).test("Helloworld");
        System.out.println("字符串符合要求吗：" + isValid);
    }
    private static void methodnot(Predicate<String> one, Predicate<String> two) {
        boolean isValid = one.or(two).test("Helloworld");
        System.out.println("字符串符合要求吗：" + isValid);
    }

    /**
     * 数组当中有多条“姓名+性别”的信息如下，请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合ArrayList 中，需要同时满足两个条件：
     * 1. 必须为女生；
     * 2. 姓名为4个字。
     */
    @Test
    void demoPredicate(){
        String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女"};
        List<String> list = filter(array, s -> "女".equals(s.split(",")[1]), s -> s.split(",")[0].length() == 4);
        System.out.println(list);
    }

    private static List<String> filter(String[] array, Predicate<String> one, Predicate<String> two) {
        List<String> list = new ArrayList<>();
        for (String info : array) {
            if (one.and(two).test(info)) {
                list.add(info);
            }
        }
        return list;
    }

    /**
     * java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据，前者称为前置条件，后者称为后置条件。
     * 抽象方法：apply
     * Function 接口中最主要的抽象方法为： R apply(T t) ，根据类型T的参数获取类型R的结果。
     * 使用的场景例如：将 String 类型转换为 Integer 类型
     */
    @Test
    void Demo11FunctionApply(){

        method1(s -> Integer.parseInt(s));

    }


    private static void method1(Function<String, Integer> function) {
        int num = function.apply("10");
        System.out.println(num + 20);
    }

    /**
     * 默认方法：andThen
     */
    @Test
    void Demo12FunctionAndThen(){
        method2(str -> Integer.parseInt(str) + 10, i -> i *= 10);
    }
    private static void method2(Function<String, Integer> one, Function<Integer, Integer> two) {
        int num = one.andThen(two).apply("10");
        System.out.println(num + 20);
    }

    /**
     * 请使用 Function 进行函数模型的拼接，按照顺序需要执行的多个函数操作为：
     * String str = "赵丽颖,20";
     * 1. 将字符串截取数字年龄部分，得到字符串；
     * 2. 将上一步的字符串转换成为int类型的数字；
     * 3. 将上一步的int数字累加100，得到结果int数字。
     */
    @Test
    void DemoFunction(){
        String str = "赵丽颖,20";
        int age = getAgeNum(str, s -> s.split(",")[1], s -> Integer.parseInt(s), n -> n += 100);
        System.out.println(age);
    }
    private static int getAgeNum(String str, Function<String, String> one, Function<String, Integer> two, Function<Integer, Integer> three) {
        return one.andThen(two).andThen(three).apply(str);
    }


}
